//
//  Calendar+Ext.swift
//  
//
//  Created by bo wang on 2020/9/30.
//  Copyright © 2020 wangbo. All rights reserved.
//

import Foundation

private let kCalendar: Calendar = {
    var c = Calendar(identifier: .gregorian)
    c.timeZone = TimeZone.current
    c.locale = Locale.autoupdatingCurrent
//    c.firstWeekday = 2
    return c
}()

extension Calendar {
        
    static var `default`: Calendar { kCalendar }
    
    enum Weekday: Int, CaseIterable {
        case sunday = 1
        case monday
        case tuesday
        case wednesday
        case thursday
        case friday
        case saturday
        
        var local: String {
            switch self {
            case .sunday: return "Sun"
            case .monday: return "Mon"
            case .tuesday: return "Tue"
            case .wednesday: return "Wed"
            case .thursday: return "Thu"
            case .friday: return "Fri"
            case .saturday: return "Sat"
            }
        }
        
        var shortSymbol: String {
            switch self {
            case .sunday: return "Su"
            case .monday: return "Mo"
            case .tuesday: return "Tu"
            case .wednesday: return "We"
            case .thursday: return "Th"
            case .friday: return "Fr"
            case .saturday: return "Sa"
            }
        }
        
        var fullSymbol: String {
            switch self {
            case .sunday: return "Sunday"
            case .monday: return "Monday"
            case .tuesday: return "Tuesday"
            case .wednesday: return "Wednesday"
            case .thursday: return "Thursday"
            case .friday: return "Friday"
            case .saturday: return "Saturday"
            }
        }
        
        var fullSymbolCN: String {
            switch self {
            case .sunday: return "周日"
            case .monday: return "周一"
            case .tuesday: return "周二"
            case .wednesday: return "周三"
            case .thursday: return "周四"
            case .friday: return "周五"
            case .saturday: return "周六"
            }
        }
        
        /// 比较两个 Weekday 的间隔天数。同一个 Weekday 返回0
        static func numberOfDays(from: Weekday, to: Weekday) -> Int {
            let dif = to.rawValue - from.rawValue
            return dif < 0 ? dif + 7 : dif
        }
        
        static func match(fullSymbol: String) -> Weekday? {
            allCases.first(where: { $0.fullSymbol == fullSymbol })
        }
    }
    
    static var secondsOfDay: TimeInterval { 24 * 60 * 60 }
    
    static var secondsOfWeek: TimeInterval { 7 * secondsOfDay }
    
    static func localString(ofWeekday weekday: Int) -> String? {
        return Weekday(rawValue: weekday)?.local
    }
    
    static func allDays(from fromDate: Date, to toDate: Date) -> [Date] {
        guard let numOfDays = kCalendar.dateComponents([.day], from: fromDate, to: toDate).day, numOfDays >= 0 else { return [] }
        let firstDay = fromDate.dayStart
        return (0...numOfDays).map({ Date(timeInterval: secondsOfDay * Double($0), since: firstDay) })
    }
    
    /// 找到指定日期后的下一个Weekday
    static func nextWeekdayDate(of weekday: Weekday, after: Date) -> Date {
        let daysDif = Weekday.numberOfDays(from: after.weekday, to: weekday)
        return after.addingTimeInterval(TimeInterval(daysDif) * secondsOfDay)
    }
}

extension DateComponents {
    func getMonthName() -> String? {
        guard let month = self.month else { return nil }

        let months = ["January", "February", "March", "April", "May", "June",
                      "July", "August", "September", "October", "November", "December"]

        let monthName = months[month - 1]

       return monthName
    }
    
    func getMonthShortName() -> String? {
        guard let month = self.month else { return nil }

        let months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
                      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]

        let monthName = months[month - 1]

       return monthName
    }
}

extension Date {
    var year: Int {
        return kCalendar.component(.year, from: self)
    }
    var month: Int {
        return kCalendar.component(.month, from: self)
    }
    var day: Int {
        return kCalendar.component(.day, from: self)
    }
    var yearMonthDay: (Int, Int, Int) {
        let components = kCalendar.dateComponents([.year, .month, .day], from: self)
        return (components.year!, components.month!, components.day!)
    }
    var hour: Int {
        return kCalendar.component(.hour, from: self)
    }
    var minute: Int {
        return kCalendar.component(.minute, from: self)
    }
    var second: Int {
        return kCalendar.component(.second, from: self)
    }
    var hourMinuteSecond: (Int, Int, Int) {
        let components = kCalendar.dateComponents([.hour, .minute, .second], from: self)
        return (components.hour!, components.minute!, components.second!)
    }
    var hourMinuteAmPmStr: String {
        let hms = hourMinuteSecond
        if hms.0 < 12 {
            return "\(hms.0, minLength: 2):\(hms.1, minLength: 2) AM"
        } else {
            return "\(hms.0 - 12, minLength: 2):\(hms.1, minLength: 2) PM"
        }
    }
    var ymdhms: (Int, Int, Int, Int, Int, Int) {
        let components = kCalendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: self)
        return (components.year!, components.month!, components.day!, components.hour!, components.minute!, components.second!)
    }
    var weekday: Calendar.Weekday {
        return Calendar.Weekday(rawValue: kCalendar.component(.weekday, from: self))!
    }
    var weekOfYear: Int {
        return kCalendar.component(.weekOfYear, from: self)
    }
    var yearForWeekOfYear: Int {
        return kCalendar.component(.yearForWeekOfYear, from: self)
    }
    var weekOfMonth: Int {
        return kCalendar.component(.weekOfMonth, from: self)
    }
    // 这个时间在当天中的总分钟数
    var minuteOfDay: Int {
        let hm = hourMinuteSecond
        return hm.0 * 60 + hm.1
    }
    // 这个时间在当天中的总秒数
    var secondOfDay: Int {
        let hm = hourMinuteSecond
        return hm.0 * 3600 + hm.1 * 60 + hm.2
    }
    // 这个时间在当天中的总小时数
    var hourOfDay: Int {
        return hourMinuteSecond.0
    }
}

extension Date {
    var monthSymbol: String {
        return kCalendar.monthSymbols[month - 1]
    }
    var shortMonthSymbol: String {
        return kCalendar.shortMonthSymbols[month - 1]
    }
}

extension Date {
    var isToday: Bool {
        return kCalendar.isDateInToday(self)
    }
    var isTomorrow: Bool {
        return kCalendar.isDateInTomorrow(self)
    }
    var isYesterday: Bool {
        return kCalendar.isDateInYesterday(self)
    }
    func isSameMinute(with date: Date) -> Bool {
        return kCalendar.isDate(self, equalTo: date, toGranularity: .minute)
    }
    func isSameHour(with date: Date) -> Bool {
        return kCalendar.isDate(self, equalTo: date, toGranularity: .hour)
    }
    func isSameDay(with date: Date) -> Bool {
        return kCalendar.isDate(self, inSameDayAs: date)
    }
    func isSameMonth(with date: Date) -> Bool {
        return kCalendar.isDate(self, equalTo: date, toGranularity: .month)
    }
    func isSameYear(with date: Date) -> Bool {
        return kCalendar.isDate(self, equalTo: date, toGranularity: .year)
    }
    var dayStart: Date {
        return kCalendar.startOfDay(for: self)
    }
    var dayEnd: Date {
        return Date(timeInterval: Calendar.secondsOfDay - 1, since: dayStart)
    }
}

extension Date {
    var startOfWeek: Date {
        var startDate: Date? = nil
        let components = kCalendar.dateComponents(Set([.yearForWeekOfYear, .weekOfYear, .weekday]), from: self)
        let weekday = components.weekday!
        if weekday == 1 {
            var components = DateComponents()
            components.day = -6
            components.second = 0
            startDate = kCalendar.date(byAdding: components, to: self)!
        } else {
            var components = DateComponents()
            components.day = -(weekday - 2)
            components.second = 0
            startDate = kCalendar.date(byAdding: components, to: self)!
        }
        
        return startDate!
    }
    
    var endTimeOfWeek: Date {
        var components = DateComponents()
        components.day = 7
        components.second = -1
        return kCalendar.date(byAdding: components, to: self.startOfWeek)!
    }
    
    var startDayOfMonth: Date {
        let components = kCalendar.dateComponents([.year, .month], from: self)
        return kCalendar.date(from: components)!
    }
    
    var endDayOfMonth: Date {
        let year = kCalendar.component(.year, from: self)
        let month = kCalendar.component(.month, from: self)
        let nextMonthFirstDay = kCalendar.date(from: DateComponents(year: year, month: month + 1, day: 1))!
        return kCalendar.date(byAdding: .day, value: -1, to: nextMonthFirstDay)!
    }
    
    var startDayOfYear: Date {
        let components = kCalendar.dateComponents([.year], from: self)
        return kCalendar.date(from: components)!
    }
    
    var endDayOfYear: Date {
        let year = kCalendar.component(.year, from: self)
        let nextYearFirstDay = kCalendar.date(from: DateComponents(year: year + 1, month: 1, day: 1))!
        return kCalendar.date(byAdding: .day, value: -1, to: nextYearFirstDay)!
    }
}
